#pragma once

#include <string>
#include <cstdio>
#include <iostream>
#include <set>

const int line_size = 1024;

class Command
{
public:
    //构造函数
    Command() {
        _white_list.insert("ls");
        _white_list.insert("pwd");
        _white_list.insert("echo");
        _white_list.insert("ls -l");
        _white_list.insert("touch");
        _white_list.insert("who");
        _white_list.insert("whoami");
    }

    bool SafeCheck(const std::string &cmdstr)
    {
        auto it = _white_list.find(cmdstr);
        //在集合中返回true，不在集合返回false
        return it == _white_list.end() ? false : true;
    }
    //给你一个命令字符串“ls -l”，让你执行，返回结果
    std::string Execute(std::string cmdstr)
    {
        // 1. pipe
        // 2. fork() + (dup2(pipe[1],1)) + exec(),将执行结果给父进程，父进程构建返回值将返回值返回给客户端
        //父进程从管道的写端，读取数据，最后返回数据给客户端
        // 3. 构建返回值
        //这个函数会在底层进行pipe，fork，dup2，exec，然后返回执行结果
        //FILE *popen(const char *command, const char *type);
        //int pclose(FILE *stream);
        //popen会在底层创建一个管道，然后fork一个子进程，子进程会执行command命令，然后将执行结果写入管道，父进程从管道中读取数据，然后返回数据给客户端
        if(!SafeCheck(cmdstr))
        {
            return std::string("Command not allowed");
        }
        FILE* fp = popen(cmdstr.c_str(), "r");
        if(fp == nullptr)
        {
            //执行失败
            perror("popen");
            return std::string("Failed to execute command");
        }
        //执行成功
        char buffer[line_size];
        std::string result;
        while(true)
        {
            char *ret = ::fgets(buffer,sizeof(buffer),fp);
            if(ret == nullptr) break;
            result += ret; //将读取到的结果追加到result中
        }
        //关闭文件指针
        pclose(fp);
        //失败返回Done，成功返回执行结果
        return result.empty() ? std::string("Done") : result; //返回执行结果
    }
private:
    //白名单，只能执行这些命令
    std::set<std::string> _white_list;
};